using System;
using System.Reflection;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace GuavaData
{
    public class ClassifierWrapper
    {
        public List<Column> input;
        public Column output;
        public string classifierCode;
        public string comments;

        public ClassifierWrapper(IEnumerable<Column> input, Column output, string code, string notes)
        {
            this.output = output;
            this.input = new List<Column>(input);
            this.classifierCode = code;
            this.comments = notes;
        }
    }

    public abstract class Classifier
    {
        public abstract object Evaluate(System.Data.DataRow dr);

        public static Classifier Build(ClassifierWrapper c)
        {
            string classText = c.classifierCode.Replace("AND", "&&"); ;
            classText = classText.Replace("OR", "||");
            classText = classText.Replace("NOT", "!");
            classText = classText.Replace("<>", "!=");

            string nullGuard = "if (";

            foreach (Column col in c.input)
            {
                string inStr = String.Concat("[[", col.Name, "]]");
                string outStr = (col.dom.NaiveType.IsClass)
                    ? String.Concat("(dr[\"", col.Name, "\"] as ", col.dom.NaiveType.FullName, ")")
                    : String.Concat("((", col.dom.NaiveType.FullName, ") dr[\"", col.Name, "\"])");
                classText = classText.Replace(inStr, outStr);

                nullGuard = String.Concat(nullGuard, "Convert.IsDBNull(dr[\"", col.Name, "\"]) ||");
            }
            
            nullGuard = String.Concat(nullGuard.Substring(0, nullGuard.Length - 3), ") return DBNull.Value;", Environment.NewLine);

            

            classText = classText.Replace("[[", "dr[\"");
            classText = classText.Replace("]]", "\"] as string");

            string[] rulelist = classText.Split(Environment.NewLine.ToCharArray());

            string built = "";
            foreach (string s in rulelist)
            {
                // Strip away leading spaces
                string sTemp = s.Trim();
                if (sTemp == null || sTemp.Length == 0) continue;

                // Prepend "true " if there is no head to the rule
                if (sTemp.Substring(0, 3).Equals("==>")) sTemp = "true " + sTemp;

                // Split on the ==> marker
                sTemp = sTemp.Replace("==>", ") return ");

                // Rebuild the rule as if(guard) return formula;
                built = String.Concat(built, "if (", sTemp, ";", Environment.NewLine);
            }

            // Add final clause to make sure a value is always returned
            built = String.Concat(built, "return null;");



            Classifier classOut = null;

            string src = String.Concat("using System;",
                "class newClass : GuavaData.Classifier",
                "{",
                "public newClass(){}",
                "public override object Evaluate(System.Data.DataRow dr)",
                "{",
                nullGuard,
                built,
                "}",
                "}");

            MessageBox.Show(src);


            Microsoft.CSharp.CSharpCodeProvider cp
                                = new Microsoft.CSharp.CSharpCodeProvider();

            System.CodeDom.Compiler.CompilerParameters cpar
                                  = new System.CodeDom.Compiler.CompilerParameters();
            cpar.GenerateInMemory = true;
            cpar.GenerateExecutable = false;
            cpar.ReferencedAssemblies.Add("System.dll");
            cpar.ReferencedAssemblies.Add("System.Windows.Forms.dll");
            cpar.ReferencedAssemblies.Add("System.Data.dll");
            cpar.ReferencedAssemblies.Add("GuavaData.dll");





            System.CodeDom.Compiler.CompilerResults cr
                                               = cp.CompileAssemblyFromSource(cpar, src);
            foreach (System.CodeDom.Compiler.CompilerError ce in cr.Errors)
                MessageBox.Show(ce.ErrorText);

            if (cr.Errors.Count == 0 && cr.CompiledAssembly != null)
            {
                Type ObjType = cr.CompiledAssembly.GetType("newClass");
                try
                {
                    if (ObjType != null)
                        classOut = (Classifier)Activator.CreateInstance(ObjType);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
                return classOut;
            }
            else
                return null;
        }



        public static System.Data.DataSet ApplyClassifiers(List<ClassifierWrapper> lcw, System.Data.DataSet ds)
        {
            System.Data.DataSet dsOut = new System.Data.DataSet();
            System.Data.DataTable dtOut = new System.Data.DataTable("queryResults");
            dsOut.Tables.Add(dtOut);
            List<Classifier> lclass = new List<Classifier>();
            foreach (ClassifierWrapper cw in lcw)
            {
                dtOut.Columns.Add(cw.output.Name, cw.output.dom.NaiveType);
                lclass.Add(Classifier.Build(cw));
            }
            

            // Loop through the rows of the data and, for each row, generate a row with the classifier output
            foreach (System.Data.DataRow dr in ds.Tables[0].Rows)
            {
                System.Data.DataRow drOut = dtOut.NewRow();

                for (int index = 0; index < lcw.Count; index++)
                    drOut[lcw[index].output.Name] = lclass[index].Evaluate(dr);

                dtOut.Rows.Add(drOut);

            }

            return dsOut;
        }

    }

}